package com.zte.claa.inficombo.csa.app.service.rptparse;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;

import javax.script.Invocable;

import org.json.JSONObject;

import com.zte.claa.inficombo.csa.app.comm.csif.CSIFCmdBuilder;
import com.zte.claa.inficombo.csa.app.comm.csif.CSIFCommService;
import com.zte.claa.inficombo.csa.app.model.dev.DataModelBean;
import com.zte.claa.inficombo.csa.app.model.dev.DevBase;
import com.zte.claa.inficombo.csa.app.model.dev.DevRunState;
import com.zte.claa.inficombo.csa.app.model.dev.DevTypeModel;
import com.zte.claa.inficombo.csa.app.model.redis.AlarmData;
import com.zte.claa.inficombo.csa.app.model.redis.DevMeteData;
import com.zte.claa.inficombo.csa.app.model.version.DevVersionUpdateLog;
import com.zte.claa.inficombo.csa.app.service.jsplugin.JSParam;
import com.zte.claa.inficombo.csa.app.service.photo.bean.PhotoFileBaseInfo;
import com.zte.claa.inficombo.csa.app.service.photo.bean.PhotoFileHBInfo;
import com.zte.claa.inficombo.csa.app.service.photo.bean.PhotoFragPacket;
import com.zte.claa.inficombo.csa.app.service.photo.bean.PhotoPackBitMapReq;
import com.zte.claa.inficombo.csa.app.service.rptparse.ext.UpDataSubParser;
import com.zte.claa.inficombo.csa.app.service.rptparse.ext.claa.ConfDataRequest;
import com.zte.claa.inficombo.csa.app.util.JsonUtils;
import com.zte.claa.inficombo.csa.app.util.StrUtils;
import com.zte.claa.inficombo.csa.app.util.XEuiUtils;


public class ParserUpdata extends ParserDefault {
	public static final String MSG_TYPE = "updata";
	
	
	// js插件解析updata返还map时的key名称
	public static final String UPD_KEY_METEDATA = "metedata";
	public static final String UPD_KEY_ALARM    = "alarm";
	public static final String UPD_KEY_ACKCMD   = "ackcmd";
	public static final String UPD_KEY_MULTIPKG = "multipkgdata";
	public static final String UPD_KEY_CONFREQ  = "claaconfreq";
	public static final String UPD_KEY_METEDATALIST = "metedatalist";
	public static final String UPD_KEY_ALARMLIST    = "alarmlist";
	public static final String UPD_KEY_LOCRAWDATA   = "locrawdata";
	public static final String UPD_KEY_DEVVERLOG    = "verupdatedata";
	public static final String UPD_KEY_GSPCURVER    = "devcurversion";
	public static final String UPD_KEY_PHOTOHBINFO    = "photofilehbinfo";

	//终端电量信息.
	protected LinkedBlockingQueue<List<DevMeteData>> devPowerDataQueue = null;
	protected boolean devPowerTaskWorkOn = true;
	
	// 覆盖父类方法.
	// protected Logger logger = LoggerFactory.getLogger(ParserUpdata.class);   Masked Field
	
	public String getType() {
		return MSG_TYPE;
	}
	
	public ParserUpdata(){
		this.initDevPowerProcessTask();	
	}
	/**
	 * 终端数据上报解析总入口。
	 * 
	 * 普通设备：
	 * {
		    "cmd": "updata", 
		    "deveui": "2C26C50001FF0001", 
		    "payload": "xxxxx",
			"port": 21
		}	 
	 */
	// @SuppressWarnings("unchecked")
	public void parseData(String jsonData, CSIFCommService svr) throws Exception {
		logger.debug("Begin parse updata: "+jsonData);
		if(jsonData == null || jsonData.trim().equals("")){
			logger.warn("ParserUpdata: jsonData is null!");
			return;
		}
		JSONObject json = new JSONObject(jsonData);
		String devEui = JsonUtils.getJsonAttrVal(json, "deveui", null);
		if(devEui == null){
			logger.warn("ParserUpdata: deveui is null: "+jsonData);
			return;
		}
		// 1.解析deEui.
		devEui = XEuiUtils.appendStartZero4xEUI(devEui);
		// 2.根据deveui---》查询缓存---》devtype，projectid，appeui等信息。
		DevBase dev = this.container.getCacheManager().getDev(devEui);
		if(dev == null){
			logger.error("[*@@*]Dev not exist in local cache/db: "+devEui);
			return;
		}
		// 添加到dev-run-state队列中，更新dev的runstate=online...
		DevRunState runState = new DevRunState();
		runState.setDevEUI(devEui);
		// 上报数据即可认为online...
		runState.setRunState(DevBase.RUN_STATE_ONLINE);
		this.container.addDevRunStateData(runState);
		// 开始解析数据...
		String devTypeId = dev.getDevType();
		// 根据devtypeid获取对应的updataSubParser，找不到时执行默认逻辑....
		UpDataSubParser updataSubParser = this.container.getUpdSubParser(devTypeId);
		if(updataSubParser != null){
			updataSubParser.parseUpData(json, dev, svr);
		} 
		// 没有定义特殊解析器的使用默认流程.
		else {
			this.parseUpRptData(json, dev, svr);
		}
	}
	
	/**
	 * 上报数据默认解析方法
	 * 
	 * @param json
	 * @param dev
	 * @param svr
	 * @throws Exception
	 * @return 0:正常解析，不分包；1-分包；
	 * 
	 */
	@SuppressWarnings("unchecked")
	protected int parseUpRptData(JSONObject json, DevBase dev, CSIFCommService svr) throws Exception {
		String devTypeId = dev.getDevType();
		// 3.根据devtype获取解析插件（缓存）
		Invocable jsRunner = this.container.getJsPnpService().getJsPLugin(devTypeId);
		if(jsRunner == null){
			logger.error("[*!!*]Can not find the JS plugin for devtype="+devTypeId);
			return 0;
		}
		// 解析其他payload数据.
		String portStr = JsonUtils.getJsonAttrVal(json, "port", "0");
		String payload = JsonUtils.getJsonAttrVal(json, "payload", null);
		String devEui = dev.getDevEui();
		// 自研lora定位产品会有detail信息.
		JSONObject detailJson = JsonUtils.getSubObject(json, "detail");
		byte[] payloadBS = CSIFCmdBuilder.base64Decode(payload);
		JSParam jsParam = new JSParam();
		jsParam.setDeveui(devEui);
		jsParam.setDevname(dev.getDevName());
		jsParam.setDevtypeid(devTypeId);
		jsParam.setDevport(portStr);
		jsParam.setAppeui(dev.getAppEui());
		jsParam.setProjectid(dev.getProjectId());
		jsParam.appendPara(JSParam.UPDATA_PARA_NAME, payloadBS);
		if(detailJson != null){
			jsParam.appendPara(JSParam.DETAIL_PARA_NAME, detailJson);
		}
		// 如果涉及分包，则尝试设置分包中间结果.
		MultiPackageData mpd = this.container.getMultiPkgData(devEui);
		if(mpd != null){
			jsParam.appendPara(JSParam.MULTIPKG_PARA_NAME, mpd);
		}		
		// 模型附加配置信息
		String devExtData = dev.getDatajson();
		if (devExtData != null && !devExtData.trim().equals("")) {
			try {
				jsParam.setDevExtDatas(JsonUtils.jsonStrToHashMap(devExtData));
			} catch (Exception err) {
				logger.error("Get devextdata failed: " + devExtData + ", err=" + err.getMessage());
			}
		}
		// 设置持久化服务...
		jsParam.setPersistSvr(this.container.getPersisService());
		Object rptData = jsRunner.invokeFunction(JSParam.UPDATA_PARSE_FUNC, jsParam);
		// 情况1--解析结果无效；
		if(rptData == null){
			logger.info("[*!*]Invalid or nouse report data!");
			return 0;
		}
		// 情况2--只有alarm数据；	
		else if(AlarmData.class.isInstance(rptData)){
			logger.debug("Alarm data return from js plugin: "+rptData);
			this.parseAlarmData((AlarmData)rptData);
			return 0;
		}		
		// 情况3--只有metedata数据；
		else if(String.class.isInstance(rptData)){
			logger.debug("Mete data return from js plugin: "+rptData.toString());
			this.parseMeteData(dev, rptData.toString(), portStr);
			return 0;
		}		
		// 情况4--同时有metedata、alarm数据、ackcmd（一种或几种兼有）；
		else if(HashMap.class.isInstance(rptData)){
			Map<String, Object> map = (HashMap<String, Object>)rptData;
			// 4.1单条告警...
			Object ao = map.get(UPD_KEY_ALARM);
			if (ao != null && AlarmData.class.isInstance(ao)) {
				logger.debug("Alarm data return from js plugin: " + ao);
				try {
					this.parseAlarmData((AlarmData) ao);
				} catch (Exception ex) {
					logger.error("parseAlarmData failed: " + ao, ex);
				}
			}
			// 4.2告警列表...
			Object aoList = map.get(UPD_KEY_ALARMLIST);
			if (aoList != null && ArrayList.class.isInstance(aoList)) {
				logger.debug("Alarm data list return from js plugin: " + aoList);
				ArrayList<AlarmData> alarmList = (ArrayList<AlarmData>) aoList;
				for (AlarmData alarm : alarmList) {
					try {
						this.parseAlarmData(alarm);
					} catch (Exception ex) {
						logger.error("parseAlarmData failed: " + alarm, ex);
					}
				}
			}		
			// 4.3单条mdata（仅限于模型字段的json字符串）...
			Object mo = map.get(UPD_KEY_METEDATA);
			if (mo != null && String.class.isInstance(mo)) {
				logger.debug("Mete data return from js plugin: " + mo);
				try {
					this.parseMeteData(dev, mo.toString(), portStr);
				} catch (Exception ex) {
					logger.error("parseMeteData failed: " + mo, ex);
				}
			}
			// 4.4mdata列表（仅限于模型字段的json字符串）...
			Object moList = map.get(UPD_KEY_METEDATALIST);
			if (moList != null && ArrayList.class.isInstance(moList)) {
				logger.debug("Mete data list return from js plugin: " + moList);
				ArrayList<String> mdataList = (ArrayList<String>) moList;
				this.parseMeteDataByBatch(dev, mdataList, portStr);
			}
			// 4.5个别设备需要回复响应确认...
			Object ackCmd = map.get(UPD_KEY_ACKCMD);
			if (ackCmd != null && AppCmdWrapper.class.isInstance(ackCmd)) {
				logger.debug("Ack command return from js plugin: " + ackCmd);
				try {
					AppCmdWrapper cmdWrapper = (AppCmdWrapper)ackCmd;
					// send cmd in async way...
					this.container.sendDevCtrlCmd(cmdWrapper);
				} catch (Exception ex) {
					logger.error("Send ack cmd failed: " + ackCmd, ex);
				}
			}
			// 4.6 claa自研设备，初次入网时请求下发配置参数...
			Object confDataReqObj = map.get(UPD_KEY_CONFREQ);
			if (confDataReqObj != null && ConfDataRequest.class.isInstance(confDataReqObj)){
				logger.info("claa dev req cmd conf data when first join: "+confDataReqObj);
				try{
					ConfDataRequest req = (ConfDataRequest)confDataReqObj;
					req.setDevtype(devTypeId);
					this.container.getDevConfDataSenderService().submitConfDataRequest(req);
				}catch(Exception ex){
					logger.error("submitConfDataRequest failed: "+confDataReqObj, ex);
				}
			}
			// 4.7 自研融合定位终端数据
			Object ldo = map.get(UPD_KEY_LOCRAWDATA);
			if (ldo != null && LocRawData.class.isInstance(ldo)) {
				logger.debug("Claa loc raw data return from js plugin: " + ldo);
				try {
					this.parseClaaLocRawData((LocRawData)ldo, dev);
				} catch (Exception ex) {
					logger.error("parseClaaLocRawData failed: " + ldo, ex);
				}
			}
			// 4.8 设备升级消息上报：包括升级准备命令响应、升级结果响应等  
			Object verupdateLog = map.get(UPD_KEY_DEVVERLOG);
			if (verupdateLog != null && DevVersionUpdateLog.class.isInstance(verupdateLog)) {
				logger.debug("Dev version update rsp return from js plugin: " + verupdateLog);
				try {
					this.processDevVerUpdateData((DevVersionUpdateLog)verupdateLog, dev);
				} catch (Exception ex) {
					logger.error("processDevVerUpdateData failed: " + verupdateLog, ex);
				}
			}	
			// 4.9 个别涉及分包情况.
			Object multiPkgObj = map.get(UPD_KEY_MULTIPKG);
			if(multiPkgObj != null && MultiPackageData.class.isInstance(multiPkgObj)){
				logger.debug("Multipackage data return from js plugin: " + multiPkgObj);
				try {
					MultiPackageData tmpMpd = (MultiPackageData)multiPkgObj;
					tmpMpd.setAppeui(dev.getAppEui());
					tmpMpd.setDeveui(devEui);
					tmpMpd.setDevName(dev.getDevName());
					tmpMpd.setDevtype(devTypeId);
					tmpMpd.setPortStr(portStr);
					tmpMpd.setProjectid(dev.getProjectId());					
					this.processMultiPkgData(tmpMpd, jsRunner, dev, portStr);
				} catch (Exception ex) {
					logger.error("Multipackage data process failed: " + multiPkgObj, ex);
				}
				return 1;
			}
			// 4.10 AID终端心跳消息处理等  
			Object photohbinfo = map.get(UPD_KEY_PHOTOHBINFO);
			if (photohbinfo != null && PhotoFileHBInfo.class.isInstance(photohbinfo)) {
				logger.info("AID dev update heartbeat message from js plugin: " + photohbinfo);
				try {
					this.processPhotoFileHBInfo((PhotoFileHBInfo)photohbinfo, dev.getDevEui());
				} catch (Exception ex) {
					logger.error("processPhotoFileHBInfo failed: " + photohbinfo, ex);
				}
			}			
		}
		// 单个分包情况，JS插件基本上不会这么返回.
		else if (MultiPackageData.class.isInstance(rptData)){
			logger.debug("Multi-package data return from js plugin: "+rptData.toString());
			MultiPackageData tmpMpd = (MultiPackageData)rptData;
			tmpMpd.setAppeui(dev.getAppEui());
			tmpMpd.setDeveui(devEui);
			tmpMpd.setDevName(dev.getDevName());
			tmpMpd.setDevtype(devTypeId);
			tmpMpd.setPortStr(portStr);
			tmpMpd.setProjectid(dev.getProjectId());	
			this.processMultiPkgData(tmpMpd, jsRunner, dev, portStr);
			return 1;
		}
		// 自研设备初次入网会请求下发参数.
		else if (ConfDataRequest.class.isInstance(rptData)){
			logger.debug("claa dev req cmd conf data when first join: "+rptData.toString());
			ConfDataRequest req = (ConfDataRequest)rptData;
			req.setDevtype(devTypeId);
			this.container.getDevConfDataSenderService().submitConfDataRequest(req);
			return 0;
		}	
		else if (LocRawData.class.isInstance(rptData)){
			logger.debug("claa loc raw data rpt: "+rptData.toString());
			this.parseClaaLocRawData((LocRawData)rptData, dev);
			return 0;
		}
		else if (DevVersionUpdateLog.class.isInstance(rptData)){
			logger.debug("claa dev version udpate data rpt: "+rptData.toString());
			this.processDevVerUpdateData((DevVersionUpdateLog)rptData, dev);
			return 0;
		}
		// 3.5 GSP设备图片上报准备命令.
		else if (PhotoFileBaseInfo.class.isInstance(rptData)){
			logger.debug("claa dev PhotoFileBaseInfo data rpt: "+rptData.toString());			
			AppCmdWrapper cmdwrapper = new AppCmdWrapper();	
			AppCommand appcmd = new AppCommand();
			try {
				this.processPhotoFileBaseInfo((PhotoFileBaseInfo)rptData, devEui);
				cmdwrapper.setAppeui(jsParam.getAppeui());
				cmdwrapper.setDeveui(jsParam.getDeveui());
				cmdwrapper.setDevtypeid(jsParam.getDevtypeid());
				cmdwrapper.setConfirm(false);
				cmdwrapper.setPort(Integer.valueOf(jsParam.getDevport()));				
				appcmd.setCmd("ReplyPhotoFileBaseInfo");				
				cmdwrapper.setPayload(appcmd);
				// send cmd in async way...
				this.container.sendDevCtrlCmd(cmdwrapper);
			}catch (Exception ex) {
				logger.error("Send PhotoFragPacket cmd failed: " + cmdwrapper.toString(), ex);
			}
			
			return 0;
		}
		// 3.6 GSP设备图片分片包处理命令.
		else if (PhotoFragPacket.class.isInstance(rptData)){
			logger.debug("claa dev PhotoFragPacket data rpt: "+rptData.toString());
			try {	
				this.processPhotoFragPacket((PhotoFragPacket)rptData, devEui);
			}catch (Exception ex) {
				logger.error("Send PhotoFragPacket cmd failed: " + devEui, ex);
			}			
			return 0;
		}
		// 3.7 GSP设备图片分片Bitmap请求命令.
		else if (PhotoPackBitMapReq.class.isInstance(rptData)){
			logger.debug("claa dev PhotoPackBitMapReq data rpt: "+rptData.toString());
			AppCmdWrapper cmdwrapper = new AppCmdWrapper();	
			AppCommand appcmd = new AppCommand();
			try {	
				cmdwrapper.setAppeui(jsParam.getAppeui());
				cmdwrapper.setDeveui(jsParam.getDeveui());
				cmdwrapper.setDevtypeid(jsParam.getDevtypeid());
				cmdwrapper.setConfirm(false);
				cmdwrapper.setPort(Integer.valueOf(jsParam.getDevport()));	
				byte[] bitmapbytes = getPhotoFragBitMap(devEui);
				if(null == bitmapbytes){
					logger.debug("claa dev PhotoPackBitMapReq, bitmapbytes is null");
					return 0;
				}
				appcmd.setCmd("ReplyPhotoFragBitMap");
				appcmd.appendPara("bitmaplen",bitmapbytes.length);
				appcmd.appendPara("bitmapbytes",StrUtils.bytes2hexstr(bitmapbytes));
				cmdwrapper.setPayload(appcmd);
				// send cmd in async way...
				this.container.sendDevCtrlCmd(cmdwrapper);
				// 生成图片
				if(true == genPhotoFile(devEui)){
					logger.info("Photofile generate successfully.SensorEui: " + devEui);
					//sendPhotoDataRecog2Aic();
				}
				
			} catch (Exception ex) {
				logger.error("Send ReplyPhotoFragBitMap cmd failed: " + cmdwrapper.toString(), ex);
			}
			return 0;
		}		
		else {
			logger.warn("[*!*]Result data is invalid from js-plugin for devtype: "+devTypeId+", data="+rptData);
		}
		return 0;
	}
	
	/**
	 * 克拉自研定位设备的解析.
	 * @param locData
	 */
	protected void parseClaaLocRawData(LocRawData locData, final DevBase dev) {
		// 子类继承该方法.
		//logger.info("Need sub-parser to process: "+locData);
		//解析终端电量数据
		try{
			List<String> datas = locData.getDevPowerData();
			if(datas != null && datas.size() > 0){
				this.pushDevPowerData2Queue(datas, dev);
			}
		}catch(Exception e){
			logger.error("[parseClaaLocRawData].device power data failed.", e);
		}
		/*解析蓝牙信标数据(信标是随终端一起报上来,js插件先将数据包装)
		 * locData.getLabelPowerData() 获取的datas中包含若干
		 * btlabel的电压等级,需要每个单独处理
		 */
		try{
			List<String> datas = locData.getLabelPowerData();			
			if(datas != null && datas.size() > 0){
				for(String data : datas){
					this.processBeaconVoltLevel(data, dev);
				}				
			}
		}catch(Exception e){
			logger.error("[parseClaaLocRawData].bt beacon volt level failed.", e);
		}
				
	}
	
	/**
	 * 自研设备升级响应消息处理.
	 * @param verUpdateLog
	 * @param dev
	 */
	protected void processDevVerUpdateData(DevVersionUpdateLog verUpdateLog, final DevBase dev) {
		try{
			verUpdateLog.setAppeui(dev.getAppEui());			
			verUpdateLog.setProjectid(dev.getProjectId());
			verUpdateLog.setDevtype(dev.getDevType());
			if(verUpdateLog.getDeveui() == null){
				verUpdateLog.setDeveui(dev.getDevEui());
			}
			int rc = this.container.getPersisService().modifyDevVersionUpdateLog(verUpdateLog);
			logger.info("[^_^]modifyDevVersionUpdateLog ok: rc="+rc+", log="+verUpdateLog);
		}catch(Exception e){
			logger.error("modifyDevVersionUpdateLog failed: "+verUpdateLog, e);
		}
	}
	
	/**
	 * 发布告警数据。
	 * 注1：如果为告警恢复消息，且告警码alarmcode为"",则需要告警接收方根据当前deveui对应的告警情况作补充设置对应的告警码。
	 * 注2：告警接收端进行告警持久化或更新（根据告警缓存数据进行告警码设置，并保证告警数据的唯一性，CSA可能多实例部署）；
	 * @param alarm
	 */
	protected void parseAlarmData(AlarmData alarm){
		logger.debug("Publish alarm: "+alarm.toString());
		String alarmJson = JsonUtils.toJson(alarm);
		this.container.getAlarmPublisher().publishMessage(alarmJson);
		logger.info("[*^_^*]Publish alarm message ok: "+alarmJson);	
	}
	
	/**
	 * 批量发布mete数据
	 * 
	 * @param dev
	 * @param rptData
	 * @param devPort
	 * @throws Exception
	 */
	protected void parseMeteDataByBatch(DevBase dev, List<String> rptDataList, String devPort) throws Exception {
		String devTypeId = dev.getDevType();
		DevTypeModel dtm = this.container.getCacheManager().getDevTypeModel(devTypeId);
		if(dtm == null || dtm.getDmList() == null){
			logger.error("[*!!*]Can not get datamodel for devtype="+devTypeId);
			return;
		}
		List<DevMeteData> dataList = new ArrayList<DevMeteData>();
		for (String rptData : rptDataList) {
			Map<String, Object> mdata = JsonUtils.jsonStrToHashMap(rptData.toString());
			DevMeteData meteData = new DevMeteData();
			meteData.setAppeui(dev.getAppEui());
			meteData.setDeveui(dev.getDevEui());
			meteData.setDevname(dev.getDevName());
			meteData.setDevtype(devTypeId);
			meteData.setPort(devPort);
			meteData.setProjectid(dev.getProjectId());
			meteData.setMdata(mdata);
			dataList.add(meteData);
		}
		// 检测是否类似北秦电气火灾设备，需要二次合并数据记录.
		if(this.container.getMeteDataRedealService().needRedeal(devTypeId)){
			this.container.getMeteDataRedealService().submitMetedatas(devTypeId, dataList);
			return;
		}		
		boolean isBatchSaveOk = true;
		try{
			this.container.getPersisService().saveDevMeteDataByBatch(dataList, dtm.getBase().getTbname());
			isBatchSaveOk = true;
		}catch(Exception e){
			isBatchSaveOk = false;
			logger.error("saveDevMeteDataByBatch failed: "+dtm.getBase().getTbname(), e);
		}
		for(DevMeteData meteData : dataList){
			try{
				// 防止批量保存时个别异常导致全部入库异常.
				if(isBatchSaveOk == false){
					this.container.getPersisService().saveDevMeteData(meteData, dtm.getBase().getTbname());
				}
				// 构造广播数据，进行redis发布...
				String pubMsg = JsonUtils.toJson(meteData);
				this.container.getUpdataPublisher().publishMessage(pubMsg);
				logger.info("[*^_^*]Publish metedata message ok: "+pubMsg);
			}catch(Exception ex){
				logger.error("saveDevMeteData failed: "+dtm.getBase().getTbname(), ex);
			}
		}		
	}
	
	/**
	 * 发布mete数据
	 * 
	 * @param dev
	 * @param rptData
	 * @param devPort
	 * @throws Exception
	 */
	protected void parseMeteData(DevBase dev, String rptData, String devPort) throws Exception {
		String devTypeId = dev.getDevType();
		DevTypeModel dtm = this.container.getCacheManager().getDevTypeModel(devTypeId);
		if(dtm == null || dtm.getDmList() == null){
			logger.error("[*!!*]Can not get datamodel for devtype="+devTypeId);
			return;
		}
		Map<String, Object> mdata = JsonUtils.jsonStrToHashMap(rptData.toString());
		DevMeteData meteData = new DevMeteData();
		meteData.setAppeui(dev.getAppEui());
		meteData.setDeveui(dev.getDevEui());
		meteData.setDevname(dev.getDevName());
		meteData.setDevtype(devTypeId);
		meteData.setPort(devPort);
		meteData.setProjectid(dev.getProjectId());
		meteData.setMdata(mdata);
		// 检测是否类似北秦电气火灾设备，需要二次合并数据记录.
		if(this.container.getMeteDataRedealService().needRedeal(devTypeId)){
			this.container.getMeteDataRedealService().submitMetedata(meteData);
			return;
		}
		// 持久化数据.
		this.container.getPersisService().saveDevMeteData(meteData, dtm.getBase().getTbname());
		// 5.构造广播数据，进行redis发布...
		String pubMsg = JsonUtils.toJson(meteData);
		this.container.getUpdataPublisher().publishMessage(pubMsg);
		logger.info("[*^_^*]Publish metedata message ok: "+pubMsg);
	}
	
	/**
	 * 一、解析框架部分的主要逻辑：
	 * 1、如果当前MPD不空，则与返回结果合并(调用container.merge方法.)；
	 * 2、如果MPD.isDataOk=yes，isParsed=yes,confirm=yes，则此时JS已经做了metedata解析和ackcmd下发；MPD从缓存删除；调外部扩展接口；
	 * 3、如果MPD.isDataOk=yes，isParsed=yes,confirm=NO，  则此时JS已经做了metedata解析，框架侧走metedata解析流程；MPD入缓存；
	 * 4、如果MPD.isDataOk=yes，isParsed=no, confirm=NO，  则此时JS未做metedata解析，触发二次解析，设置isparsed=yes；MPD入缓存；
	 * 5、如果MPD.isDataOk=yes，isParsed=no, confirm=yes，则此时JS未做metedata解析，触发二次解析，设置isparsed=yes；MPD入缓存；
	 * 6、如果MPD.isDataOk=NO，confirm=yes，则此时JS已经做了ackcmd下发，框架侧走相应ACK流程；MPD入缓存；
	 * 7、如果MPD.isDataOk=NO，confirm=NO，此时MPD入缓存，等待后续包；
	 * 
	 * 二、相应地JS插件内部逻辑：（注意确认包序号是从1开始还是从0开始，JS-Framework约定从1开始）
	 * 1、接收原始byte[]流-bs，并尝试从JSParam中获取multipkgdata参数-mpd；
	 * 2、如果mpd参数为空，则创建一个mpd对象；
	 * 3、当前bs是普通分包数据包，则解析分包数据，确定包序号；
	 * 	   3.1 将包append到mpd中，注意：判断是否出现重复包，如果出现则将mpd重新初始化；
	 * 	   3.2 判断mpd是否dataok；
	 *     3.3 如果dataok，则调用buildData()解析出metedata（本次GNSS设备不需要，其他普通分包类型的情况需要），设置isparsed=yes；
	 *     3.4返回mpd及metedata（本次GNSS不需要），特别地，如果GNSS服务器只需要GNSS终端的净荷，还涉及data的特殊处理.
	 * 4、当前bs是分包上行校验包（分包发送完毕后的确认消息）；
	 *	   4.1 设置mpd的confirmed值=1；
	 *	   4.2 获取确认位图（getBitmap）及构造ackcmd，使用全1模式（ignoreabsentpkg=true）；
	 *	   4.3返回mpd和ackcmd；
	 * 5、新增二次解析方法入口： UPDATA_MULTIPKG_REPARSE_FUNC = "funcmultipkgreparse";
	 * 	   5.1框架针对分包的类型调用，入参是mpd；
	 *     5.2调用buildData()，得到完整码流，解析码流返回metedata（可选）和mpd（设置isparsed=yes）；
	 */
	protected void processMultiPkgData(MultiPackageData mpd, Invocable jsRunner, DevBase dev, String portStr) throws Exception {
		if(mpd == null){
			return;
		}
		MultiPackageData mergeData = this.container.mergePackageData(mpd);
		// 加锁互斥，避免重复处理.
		synchronized (mergeData) {
			boolean isConfirmed = (mergeData.getIsConfirmed() == MultiPackageData.CONFIRMED_YES);
			boolean isDataOk = mergeData.isDataOk();
			boolean isParsed = (mergeData.getIsParsed() == MultiPackageData.PARSED_YES);
			if (isDataOk && isParsed && isConfirmed) {
				this.container.removeMultiPkgData(mpd.getDeveui());
				logger.info("[1].remove MultiPkgData: deveui="+mpd.getDeveui());
			} else if (isDataOk && !isParsed) {
				// 首先设置解析标记.
				mergeData.setIsParsed(MultiPackageData.PARSED_YES);
				this.container.setMultiPkgData(mergeData);
				// 分包收集完整后进行二次解析
				this.reParseMultiPkgData(mergeData, jsRunner, dev, portStr);
				// 解析结束，删除组包对象.
				this.container.removeMultiPkgData(mpd.getDeveui());
				logger.info("[2].finish MultiPkgData: deveui="+mpd.getDeveui());
			} else if (isDataOk && isParsed && !isConfirmed) {
				this.container.setMultiPkgData(mergeData);
				logger.debug("[3].reParseMultiPkgData: "+mpd);
			} else if (!isDataOk && isConfirmed) {
				this.container.setMultiPkgData(mergeData);
				logger.debug("[4].reParseMultiPkgData: "+mpd);
			} else if (!isDataOk && !isConfirmed) {
				this.container.setMultiPkgData(mergeData);
				logger.debug("[5].reParseMultiPkgData: "+mpd);
			}
		}
	}

	/**
	 * 分包收集完整后进行补充二次解析
	 * 
	 * @param mpd
	 * @param jsRunner
	 */
	@SuppressWarnings("unchecked")
	protected void reParseMultiPkgData(MultiPackageData mpd, Invocable jsRunner, DevBase dev, String portStr) throws Exception {
		// 设置可能的扩展配置数据，供js插件访问.
		this.setExtDataToMultiPkg(mpd, dev);
		// 开始二次解析.
		Object rptData = jsRunner.invokeFunction(JSParam.UPDATA_MULTIPKG_REPARSE_FUNC, mpd);
		// 情况1--解析结果无效；
		if(rptData == null){
			logger.info("[*!*]MPD-Reparse: Invalid or nouse report data!");
			return;
		}
		// 情况2--只有alarm数据；	
		else if(AlarmData.class.isInstance(rptData)){
			logger.debug("MPD-Reparse: Alarm data return from js plugin: "+rptData);
			this.parseAlarmData((AlarmData)rptData);
			return;
		}		
		// 情况3--只有metedata数据；
		else if(String.class.isInstance(rptData)){
			logger.debug("MPD-Reparse: Mete data return from js plugin: "+rptData.toString());
			this.parseMeteData(dev, rptData.toString(), portStr);
			return;
		}		
		// 情况3.5 自研设备初次入网会请求下发参数.
		else if (ConfDataRequest.class.isInstance(rptData)){
			logger.debug("claa dev req cmd conf data when first join: "+rptData.toString());
			ConfDataRequest req = (ConfDataRequest)rptData;
			req.setDevtype(dev.getDevType());
			this.container.getDevConfDataSenderService().submitConfDataRequest(req);
		}	
		// 情况4--同时有metedata、alarm数据、ackcmd（一种或几种兼有）；
		else if(HashMap.class.isInstance(rptData)){
			Map<String, Object> map = (HashMap<String, Object>)rptData;
			// 4.1单条告警...
			Object ao = map.get(UPD_KEY_ALARM);
			if (ao != null && AlarmData.class.isInstance(ao)) {
				logger.debug("Alarm data return from js plugin: " + ao);
				try {
					this.parseAlarmData((AlarmData) ao);
				} catch (Exception ex) {
					logger.error("parseAlarmData failed: " + ao, ex);
				}
			}
			// 4.2告警列表...
			Object aoList = map.get(UPD_KEY_ALARMLIST);
			if (aoList != null && ArrayList.class.isInstance(aoList)) {
				logger.debug("Alarm data list return from js plugin: " + aoList);
				ArrayList<AlarmData> alarmList = (ArrayList<AlarmData>) aoList;
				for (AlarmData alarm : alarmList) {
					try {
						this.parseAlarmData(alarm);
					} catch (Exception ex) {
						logger.error("parseAlarmData failed: " + alarm, ex);
					}
				}
			}		
			// 4.3单条mdata（仅限于模型字段的json字符串）...
			Object mo = map.get(UPD_KEY_METEDATA);
			if (mo != null && String.class.isInstance(mo)) {
				logger.debug("Mete data return from js plugin: " + mo);
				try {
					this.parseMeteData(dev, mo.toString(), portStr);
				} catch (Exception ex) {
					logger.error("parseMeteData failed: " + mo, ex);
				}
			}
			// 4.4mdata列表（仅限于模型字段的json字符串）...
			Object moList = map.get(UPD_KEY_METEDATALIST);
			if (moList != null && ArrayList.class.isInstance(moList)) {
				logger.debug("Mete data list return from js plugin: " + moList);
				ArrayList<String> mdataList = (ArrayList<String>) moList;
				this.parseMeteDataByBatch(dev, mdataList, portStr);
			}
			// 4.5个别设备需要回复响应确认...
			Object ackCmd = map.get(UPD_KEY_ACKCMD);
			if (ackCmd != null && AppCmdWrapper.class.isInstance(ackCmd)) {
				logger.debug("Ack command return from js plugin: " + ackCmd);
				try {
					AppCmdWrapper cmdWrapper = (AppCmdWrapper)ackCmd;
					// send cmd in async way...
					this.container.sendDevCtrlCmd(cmdWrapper);
				} catch (Exception ex) {
					logger.error("Send ack cmd failed: " + ackCmd, ex);
				}
			}
			// 4.6 claa自研设备，初次入网时请求下发配置参数...
			Object confDataReqObj = map.get(UPD_KEY_CONFREQ);
			if (confDataReqObj != null && ConfDataRequest.class.isInstance(confDataReqObj)){
				logger.debug("claa dev req cmd conf data when first join: "+confDataReqObj);
				try{
					ConfDataRequest req = (ConfDataRequest)rptData;
					req.setDevtype(dev.getDevType());
					this.container.getDevConfDataSenderService().submitConfDataRequest(req);
				}catch(Exception ex){
					logger.error("submitConfDataRequest failed: "+confDataReqObj, ex);
				}
			}
			// 4.8 自研融合定位终端数据
			Object ldo = map.get(UPD_KEY_LOCRAWDATA);
			if (ldo != null && LocRawData.class.isInstance(ldo)) {
				logger.debug("Claa loc raw data return from js plugin: " + ldo);
				try {
					this.parseClaaLocRawData((LocRawData)ldo, dev);
				} catch (Exception ex) {
					logger.error("parseClaaLocRawData failed: " + ldo, ex);
				}
			}
		}
		else if (LocRawData.class.isInstance(rptData)){
			logger.debug("[2]-claa loc raw data rpt: "+rptData.toString());
			this.parseClaaLocRawData((LocRawData)rptData, dev);
		}
		else {
			logger.warn("[*!*]Result data is invalid from js-plugin for devtype: "+dev.getDevType()+", data="+rptData);
		}
	}
	
	/**
	 * 分包时扩增配置数据的设置.
	 */
	protected void setExtDataToMultiPkg(MultiPackageData mpd, DevBase dev) {
		try {
			String devExtData = dev.getDatajson();
			if (devExtData == null || devExtData.trim().equals("")) {
				return;
			}
			mpd.setDevExtDatas(JsonUtils.jsonStrToHashMap(devExtData));
		} catch (Exception e) {
			logger.error("setExtDataToMultiPkg failed: " + dev + ", err=" + e.getMessage());
		}
	}
	
	/**
	 * 从js的解析结果中获取各上报数据项.
	 * 
	 * @param rptData
	 * @param dtm
	 * @return
	 * @deprecated
	 */
	protected Map<String, Object> getMeteData(String rptData, DevTypeModel dtm) throws Exception {
		JSONObject json = new JSONObject(rptData);
		Map<String, Object> mdata = new HashMap<String, Object>();
		 List<DataModelBean> dmbList = dtm.getDmList();
		 for(DataModelBean dmb : dmbList){
			String fn = dmb.getFieldname();
			String fv = JsonUtils.getJsonAttrVal(json, fn, null);
			byte dt = dmb.getDatatype();
			switch(dt){
			case DataModelBean.DT_BYTE:
				if(fv != null){
					mdata.put(fn, Byte.parseByte(fv.trim()));
				} 
				break;
			case DataModelBean.DT_SHORT:
				if(fv != null){
					mdata.put(fn, Short.parseShort(fv.trim()));
				} 
				break;
			case DataModelBean.DT_INT:
				if(fv != null){
					mdata.put(fn, Integer.parseInt(fv.trim()));
				} 
				break;
			case DataModelBean.DT_LONG:
				if(fv != null){
					mdata.put(fn, Long.parseLong(fv.trim()));
				} 
				break;
			case DataModelBean.DT_FLOAT:
				if(fv != null){
					mdata.put(fn, Float.parseFloat(fv.trim()));
				} 
				break;
			case DataModelBean.DT_DOUBLE:
				if(fv != null){
					mdata.put(fn, Double.parseDouble(fv.trim()));
				} 
				break;
			default:
				mdata.put(fn, fv);
				break;
			}
		 }
		return mdata;
	}
	
	
	
	//设备电量管理任务线程
	protected void initDevPowerProcessTask(){		
		this.devPowerDataQueue = new LinkedBlockingQueue<List<DevMeteData>>(10000);	
		this.devPowerTaskWorkOn = true;
		Thread devPowerProcessTask = new Thread(new Runnable() {
			@Override
			public void run() {
				launchDevPowerService();
			}
		});
		devPowerProcessTask.start();	
		logger.info("DevPowerProcessTask thread started: DevPowerProcessTask");
	} 
	
	
	//设备电量管理
	protected void launchDevPowerService(){
		List<DevMeteData> dataList = null;
		
		while(this.devPowerTaskWorkOn){			
			try{								
				dataList = this.devPowerDataQueue.poll(20, TimeUnit.SECONDS);			
				if(dataList == null){
					continue;
				} 								
				this.container.getDevPowerDataService().processDevPowerData(dataList);				
				if(dataList.size() > 0){			
					//电量计算完成后入库
					this.container.getPersisService().saveLocRawDataByBatch(dataList, LocRawData.DEVICE_POWER_DATA);
				}						
			}catch (Throwable t) {
				logger.error("Dev power data save failed:{}",t.getMessage());			
			}			
		}
		logger.warn("devPowerService thread exit: " + Thread.currentThread().getName());
	}
		
		
		
	
	//包装蓝牙信标的电压等级数据
	protected void processBeaconVoltLevel(String jsonData , DevBase dev){
		if(jsonData == null){
			return ;
		}
	
		List<DevMeteData> dataList = new ArrayList<>();	
		Map<String, Object> mdata = JsonUtils.jsonStrToHashMap(jsonData.toString());
		//这里使用remove方法后mdata只剩下电压等级,与DB模型对齐
		Object labeleui = mdata.remove("labeleui");
		if(labeleui != null){			
			DevMeteData meteData = new DevMeteData();
			meteData.setAppeui(dev.getAppEui());
			meteData.setDeveui((String)labeleui);
			meteData.setDevtype("claa_btlabel");
			meteData.setPort("0");
			meteData.setProjectid(dev.getProjectId());
			meteData.setMdata(mdata);
			dataList.add(meteData);
		}
		Object uwbBeaconEui = mdata.remove("uwbbeaconeui");
		if(uwbBeaconEui != null){			
			DevMeteData meteData = new DevMeteData();
			meteData.setAppeui(dev.getAppEui());
			meteData.setDeveui((String)uwbBeaconEui);
			meteData.setDevtype("claa_uwbbeacon");
			meteData.setPort("0");
			meteData.setProjectid(dev.getProjectId());
			meteData.setMdata(mdata);
			dataList.add(meteData);
		}	
		//将信标电压等级信息入库
		if(dataList != null && dataList.size() > 0){
			boolean b = this.devPowerDataQueue.offer(dataList);
			if(!b){
				logger.warn("Add bt beacon volt level to queue failed: " + dataList.get(0).getDeveui());
			}
		}
	}
		
	
	//将原始电量信息入队列
	protected void pushDevPowerData2Queue(List<String> jsonDatas, final DevBase dev){
		if(jsonDatas == null){
			return;
		}
	
		List<DevMeteData> dataList = new ArrayList<>();	
		for (String rptData : jsonDatas) {
			Map<String, Object> mdata = JsonUtils.jsonStrToHashMap(rptData.toString());
			if(mdata == null){
				continue;
			}
			mdata.put("projectid", dev.getProjectId());
			DevMeteData meteData = new DevMeteData();
			meteData.setAppeui(dev.getAppEui());
			meteData.setDeveui(dev.getDevEui());
			meteData.setDevname(dev.getDevName());
			meteData.setDevtype(dev.getDevType());
			meteData.setPort("0");
			meteData.setProjectid(dev.getProjectId());
			meteData.setMdata(mdata);
			dataList.add(meteData);
		}
		//电量原始数据存入队列
		if(dataList != null && dataList.size() > 0){
			boolean b = this.devPowerDataQueue.offer(dataList);
			if(!b){
				logger.warn("Add dev power data to queue failed: " + dataList.get(0).getDeveui());
			}
		}
	}
		
	/**
	* 处理GSP设备图片上报准备命令.
	*/
	private void processPhotoFileBaseInfo(PhotoFileBaseInfo photoBase, String sensorEui){
		this.container.getPhotoDataDealService().dealAIDBaseInfo(sensorEui, photoBase);
	}

	/**
	* 处理GSP设备图片上报准备命令.
	*/
	private void processPhotoFileHBInfo(PhotoFileHBInfo photohb, String sensorEui){
		this.container.getPhotoDataDealService().dealAIDHeartBeatMsg(sensorEui, photohb);
	}
	
	/**
	* 处理GSP设备图片上报准备命令.
	*/
	private void processPhotoFragPacket(PhotoFragPacket fragPacket, String sensorEui){
		this.container.getPhotoDataDealService().insertPhotoFragPacket(sensorEui, fragPacket);
	}
	
	/**
	* 处理GSP设备图片上报准备命令.
	*/
	private byte[] getPhotoFragBitMap(String sensorEui){
		return this.container.getPhotoDataDealService().getPhotoFragBitMap(sensorEui);
	}
	
	
	/**
	* 图片如果生成图片.
	 * @throws IOException 
	*/
	private boolean genPhotoFile(String sensorEui) throws IOException{
		return this.container.getPhotoDataDealService().genPhotoFile(sensorEui);
	}
}
